﻿using System;
using System.Collections.Generic;

namespace Borg.Framework.CompositionBinding
{
    public class SelectBehavior : ICompositionBehavior<PlugInfo>
    {
        private readonly List<PlugInfo> _items;
        private SlotInfo _slot;

        public event EventHandler<ItemEventArgs<PlugInfo>> ItemAdded;
        public event EventHandler<ItemEventArgs<PlugInfo>> ItemRemoved;

        public event EventHandler<ItemEventArgs<PlugInfo>> ItemComposed;
        public event EventHandler<ItemEventArgs<PlugInfo>> ItemDecomposed;

        public SelectBehavior()
        {
            _items = new List<PlugInfo>();
        }

        public SelectBehavior(SlotInfo slot)
        {
            _items = new List<PlugInfo>();
            Slot = slot;
        }

        private void Add(PlugInfo item)
        {
            _items.Add(item);
            OnEvent(ItemAdded, new ItemEventArgs<PlugInfo>(item));
        }

        public void Compose(PlugInfo item)
        {
            if (_slot != null)
                _slot.SelectPlugs(new[] {item});
        }

        public void Decompose(PlugInfo item)
        {
            if (_slot != null)
                _slot.DeselectPlugs(new[] {item});
        }

        private void OnEvent(EventHandler<ItemEventArgs<PlugInfo>> handler, ItemEventArgs<PlugInfo> args)
        {
            if (handler != null)
                handler(this, args);
        }

        private void OnPlugged(object sender, PlugEventArgs args)
        {
            Add(args.PlugInfo);
        }

        private void OnSelectionChanged(object sender, SelectionChangedEventArgs args)
        {
            foreach (var pi in args.RemovedPlugs)
                OnEvent(ItemDecomposed, new ItemEventArgs<PlugInfo>(pi));

            foreach (var pi in args.AddedPlugs)
                OnEvent(ItemComposed, new ItemEventArgs<PlugInfo>(pi));
        }

        private void OnUnplugged(object sender, PlugEventArgs args)
        {
            Remove(args.PlugInfo);
        }

        private void Remove(PlugInfo item)
        {
            _items.Remove(item);
            OnEvent(ItemRemoved, new ItemEventArgs<PlugInfo>(item));
        }

        public IEnumerable<PlugInfo> ComposedItems
        {
            get
            {
                return _slot != null
                    ? _slot.SelectedPlugs
                    : new List<PlugInfo>();
            }
        }

        public IEnumerable<PlugInfo> Items
        {
            get { return _items; }
        }

        public SlotInfo Slot
        {
            get { return _slot; }
            set
            {
                if (_slot == value)
                    return;

                if (_slot != null && _slot != null)
                {
                    _slot.Plugged -= OnPlugged;
                    _slot.Unplugged -= OnUnplugged;
                    _slot.SelectionChanged -= OnSelectionChanged;

                    foreach (var pi in _slot.SelectedPlugs)
                        OnEvent(ItemDecomposed, new ItemEventArgs<PlugInfo>(pi));

                    foreach (var pi in _slot.PluggedPlugInfos)
                        Remove(pi);
                }

                _slot = value;

                if (_slot == null) return;

                _slot.Plugged += OnPlugged;
                _slot.Unplugged += OnUnplugged;
                _slot.SelectionChanged += OnSelectionChanged;

                foreach (var pi in _slot.PluggedPlugInfos)
                    Add(pi);

                foreach (var pi in _slot.SelectedPlugs)
                    OnEvent(ItemComposed, new ItemEventArgs<PlugInfo>(pi));
            }
        }
    }
}

